package com.example.lib.server;


import com.example.lib.score.Score;
import com.example.lib.userinfo.UserInfo;

import net.sf.json.JSONException;
import net.sf.json.JSONObject;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;


public class Server {
    private static final String TAG = "Server";
    private String content = "";
    /**
     * 对战人数
     */
    private final int allowUsers = 2;
    /**
     * 开始游戏标志
     */
    private boolean startGame = false;

    private List<UserInfo> userInfoList;
    private List<Score> scoreList;
    private HashMap<String,String> selectpattern;
    private static HashSet<Socket> cons;
    private String gamepattern;

    private final Object object = "";
    private final Object lockScore1 = "";
    private final Object lockScore2 = "";

    private boolean user1Over = false;
    private boolean user2Over = false;

    private boolean score1Changed = false;
    private boolean score2Changed = false;

    private int overCnt = 0;

    public static void main(String args[]){
        new Server();
    }
    public  Server(){
        userInfoList = new ArrayList<>();
        scoreList = new ArrayList<>();
        selectpattern = new HashMap<>();
        cons = new HashSet<>();
        try{
            InetAddress addr = InetAddress.getLocalHost();
            System.out.println("local host:" + addr);

            //创建server socket
            ServerSocket serverSocket = new ServerSocket(9999);
            System.out.println("listen port 9999");

            while(true){
                System.out.println("waiting client connect");
                Socket socket = serverSocket.accept();
                System.out.println("accept client connect" + socket);
                new Thread(new Service(socket)).start();
                if (cons.size() < 2) {
                    cons.add(socket);
                }
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    class Service implements Runnable{
        private Socket socket;
        private BufferedReader in = null;
        private int state = 0;
        private String request;
        private String username;
        private String password;
        private String msg;
        private String pattern;

        public Service(Socket socket){
            this.socket = socket;
            try{
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            }catch (IOException ex){
                ex.printStackTrace();
            }
        }

        @Override
        public void run() {
            System.out.println("wait client message ");
            try {
                while ((content = in.readLine()) != null) {
                    if (startGame) {
                        start();
                        break;
                    }
                    System.out.println("message from client:" + content);
                    JSONObject jsonObject = JSONObject.fromObject(content);
                    if (jsonObject != null) {
                        request = jsonObject.getString("request");
                        System.out.println("request = " + request);
                    }
                    switch (request) {
                        //注册请求
                        case "register":
                                username = jsonObject.getString("username");
                                password = jsonObject.getString("password");
                                msg = Register.register(username, password);
                                if (msg.equals("success") && userInfoList.size() < allowUsers) {
                                    userInfoList.add(new UserInfo(username, password));
                                }
                                sendMessage(socket,msg);
                                ready();
                            break;
                        //登录请求
                        case "login":
                                System.out.println(content);
                                username = jsonObject.getString("username");
                                password = jsonObject.getString("password");
                                msg = Login.login(username, password);
                                System.out.println(msg);
                                if (msg.equals("success") && userInfoList.size() < allowUsers) {
                                    userInfoList.add(new UserInfo(username, password));
                                    sendMessage(socket,msg);
                                    ready();
                                } else {
                                    sendMessage(socket,msg);
                                }
//                            }
                            break;
                        case "bye":
                            userInfoList.clear();
                            scoreList.clear();
                            System.out.println(content + "disconnect from client,close socket");
                            cons.remove(socket);
                            socket.shutdownInput();
                            socket.shutdownOutput();
                            socket.close();
                            break;
                        case "changepattern":
                            try {
                                    username = jsonObject.getString("username");
                                    pattern = jsonObject.getString("selectpattern");
                                    if (!"easy".equals(pattern) && !"normal".equals(pattern) && !"hard".equals(pattern)) {
                                        continue;
                                    }
                                    for (Socket con : cons) {
                                        PrintWriter pout = new PrintWriter(new BufferedWriter(
                                                new OutputStreamWriter(con.getOutputStream(),"utf-8")),true);
                                        pout.println(initJsonselect(username,pattern).toString());
                                    }
                                    synchronized (selectpattern) {
                                        if (selectpattern.containsKey(username)) {
                                            selectpattern.replace(username,pattern);
                                        } else {
                                            selectpattern.put(username,pattern);
                                        }
                                        if (Objects.equals(selectpattern.get(userInfoList.get(0).getName()), selectpattern.get(userInfoList.get(1).getName()))) {
                                            gamepattern = selectpattern.get(userInfoList.get(0).getName());
                                            System.out.println("gamepattern = " + gamepattern);
                                            for (Socket con : cons) {
                                                sendMessage(con,"startgame");
                                            }
                                            startGame = true;
                                        }
                                    }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                            break;
                        default:
                            break;
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        private JSONObject initJsonselect(String userName,String selectpattern) throws JSONException {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("msg", "select");
            jsonObject.put("username", userName);
            jsonObject.put("selectpattern", selectpattern);
            return jsonObject;
        }

        /**
         * 等待对方连接
         */
        private void ready() {
            synchronized (object) {
                while (userInfoList.size() != allowUsers) {
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                object.notify();
            }
            scoreList.add(new Score(userInfoList.get(0).getName(),0,false));
            scoreList.add(new Score(userInfoList.get(1).getName(),0,false));
            for (Socket con : cons) {
                sendMessage(socket,"choosepattern");
            }
        }

        /**
         * After choosing difficulty, start playing!
         */
        private void start() {
            System.out.println("start game!");
            new Thread(receiveScore).start();
            new Thread(forwardScore).start();
        }

        Runnable receiveScore = () -> {
            try {
                String reply;
                while ((reply = in.readLine()) != null) {
                    JSONObject jsonObject = JSONObject.fromObject(reply);
                    String username = jsonObject.getString("username");
                    int score = jsonObject.getInt("score");
                    System.out.println(scoreList.get(0).getScore());
                    System.out.println(scoreList.get(1).getScore());
                    System.out.println(score);
                    System.out.println(username);
                    boolean gameoverflag = jsonObject.getBoolean("gameoverflag");
                    if (username.equals(userInfoList.get(0).getName())) {
                        scoreList.get(0).setScore(score);
                        scoreList.get(0).setGameOverFlag(gameoverflag);
                        if (scoreList.get(0).isGameOverFlag()) {
                            synchronized (scoreList.get(1)) {
                                System.out.println(scoreList.get(0).getUserName() + "over");
                                overCnt++;
                                score2Changed = true;
                                scoreList.get(1).notify();
                            }
                            return;
                        }
                    } else {
                        scoreList.get(1).setScore(score);
                        scoreList.get(1).setGameOverFlag(gameoverflag);
                        if (scoreList.get(1).isGameOverFlag()) {
                            synchronized (scoreList.get(0)) {
                                System.out.println(scoreList.get(1).getUserName() + "over");
                                overCnt++;
                                scoreList.get(0).notify();
                            }
                            return;
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        };

        Runnable forwardScore = () -> {
            while (true) {
                synchronized (object) {
                    if (overCnt == allowUsers) {
                        JSONObject score1 = initJsonscore("over",scoreList.get(0));
                        JSONObject score2 = initJsonscore("over",scoreList.get(1));
                        for (Socket con : cons) {
                            PrintWriter pout = null;
                            try {
                                pout = new PrintWriter(new BufferedWriter(
                                        new OutputStreamWriter(con.getOutputStream(),"utf-8")),true);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                            pout.println(score1.toString());
                            pout.println(score2.toString());
//                            userInfoList.clear();
//                            scoreList.clear();
//                            System.out.println(content + "disconnect from client,close socket");
//                            cons.remove(con);
//                            try {
//                                con.shutdownInput();
//                                con.shutdownOutput();
//                                con.close();
//                            } catch (IOException e) {
//                                e.printStackTrace();
//                            }
                        }
                        break;
                    }
                }
                JSONObject score1 = initJsonscore("game",scoreList.get(0));
                JSONObject score2 = initJsonscore("game",scoreList.get(1));
                for (Socket con : cons) {
                    PrintWriter pout = null;
                    try {
                        pout = new PrintWriter(new BufferedWriter(
                                new OutputStreamWriter(con.getOutputStream(),"utf-8")),true);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    pout.println(score1.toString());
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for (Socket con : cons) {
                    PrintWriter pout = null;
                    try {
                        pout = new PrintWriter(new BufferedWriter(
                                new OutputStreamWriter(con.getOutputStream(),"utf-8")),true);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    pout.println(score2.toString());
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        private JSONObject initJsonscore(String msg,Score score) throws JSONException {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("msg",msg);
            jsonObject.put("username",score.getUserName());
            jsonObject.put("score",score.getScore());
            jsonObject.put("gameoverflag",score.isGameOverFlag());
            return jsonObject;
        }

        private JSONObject initJsonmsg(String msg) throws JSONException {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("msg",msg);
            return jsonObject;
        }

        public void sendMessage(Socket socket,String message) {
            PrintWriter pout = null;
            try{
                System.out.println("messge to client:" + message);
                pout = new PrintWriter(new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream(),"utf-8")),true);
                pout.println(initJsonmsg(message).toString());
            }catch (IOException ex){
                ex.printStackTrace();
            }
        }
    }
}